
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.Xml;

using Swaf;


namespace Swaf.Gui
{
	/// <summary>
	/// Summary description for BaseSpreadsheetLayout.
	/// </summary>
	public abstract class BaseSpreadsheetLayout : BaseLayoutManager
	{
		protected SpreadsheetLayoutAlg m_layout;
		protected IDictionary m_namedCells;

		public BaseSpreadsheetLayout()
		{
			m_layout = new SpreadsheetLayoutAlg(new SpreadsheetLayoutAlg.CreateRowCol(rowColFactory));
			setDefaultCellSizes(5,5);
			m_namedCells = new Hashtable();
		}

		public override IGuiWidget getNamedItem(string name)
		{
			IGuiWidget item = null;
			SpreadsheetCell cell = (SpreadsheetCell)m_namedCells[NameAddress.getFirstAddress(name)];
			if(cell != null)
			{
				item = cell.Child;
				if(!NameAddress.isSingleAddress(name) && item is ILayoutManager)
					item = ((ILayoutManager)item).getNamedItem(NameAddress.getRemainingAddress(name));
			}

			return item;
		}

		protected abstract RowCol rowColFactory(int pos);

		public void setRowExpandable(int rowNum)
		{
			setRowExpandable(rowNum, true);
		}

		public void setRowExpandable(int rowNum, bool expand)
		{
			m_layout.getRowCol(RowColType.rowType, rowNum).Expandable = expand;
		}

		public void setColExpandable(int colNum)
		{
			setColExpandable(colNum, true);
		}

		public void setColExpandable(int colNum, bool expand)
		{
			m_layout.getRowCol(RowColType.colType, colNum).Expandable = expand;
		}

		public void setDefaultCellSizes(int w, int h)
		{
			m_layout.setDefaultCell(new NumericPair(w,h));
		}

		public override XmlElement LayoutTemplate
		{
			get
			{
				return base.LayoutTemplate;
			}
			set
			{
				base.LayoutTemplate = value;

				if(m_origionalTemplate.Name != "SpreadsheetLayout")
					throw new Exception("Invalid m_origionalTemplate format");
				string[,] attrvalues = new string[,]{{"defaultWidth", "5"},{"defaultHeight", "5"}};
				Cmn.getAttributes(m_origionalTemplate, attrvalues, false);
				setDefaultCellSizes(Int32.Parse(attrvalues[0,1]), Int32.Parse(attrvalues[1,1]));

				foreach(XmlElement child in m_origionalTemplate.ChildNodes)
				{
					if(child.Name == "Cell")
					{
						createChildCellFromTemplate(child);
					}
					else if(child.Name == "ColInfo" || child.Name == "RowInfo")
					{	
						attrvalues = new string[,]{{"position", "-1"},{"expandable", "true"}};
						Cmn.getAttributes(child, attrvalues, false);
						int pos = Int32.Parse(attrvalues[0,1]);
						bool exp = Boolean.Parse(attrvalues[1,1]);
						if(pos <= 0)
							throw new Exception("Invalid col/row position: must be > 0:" + attrvalues[0,1]);
						if(child.Name == "ColInfo")
							this.setColExpandable(pos, exp);
						else
							this.setRowExpandable(pos, exp);
					}
				}
			}
		}


		protected void createChildCellFromTemplate(XmlElement cellInfo)
		{
			if(cellInfo.Name == "Cell")
			{
				string[,] attrvalues = new string[,]{
					{"x", "-1"}, {"y", "-1"}, {"w", "1"}, {"h", "1"},{"style", ""},{"id",""}};
				Cmn.getAttributes(cellInfo, attrvalues, false);
				int x = Int32.Parse(attrvalues[0,1]);
				int y = Int32.Parse(attrvalues[1,1]);
				int w = Int32.Parse(attrvalues[2,1]);
				int h = Int32.Parse(attrvalues[3,1]);
				string cellStyle = attrvalues[4,1];
				string cellName = attrvalues[5,1];

				if(x <=0 || y <= 0 || w <= 0 || h <= 0)
					throw new Exception("Bad params in Cell definition: " + cellInfo.OuterXml);
				SpreadsheetCell cell = addChild(new Rect(x,y,w,h), cellStyle, cellName);

				//Check for a child definition a process in coordination with the my child implementation.
				XmlElement childInfo = (XmlElement)cellInfo.FirstChild;
				if(childInfo != null)
				{
					IGuiWidget cellChild = Application.currentApp.guiMgr.createWidget(childInfo);
					if(cellChild != null)
					{
						ILocation loc = this.createLocation(cellName,null); 
						if(loc != null && loc.ParentElement != null)
							cellChild.show(loc);
						cellChild.ParentLayout = this;

						cell.Child = cellChild;
					}
					else
					{
						//TODO: A bad widget definition was embedded in the template - How
						//should the code react?  Throw an exception or just make a log entry.
						Application.currentApp.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"Unable to create widget from template '{1}'", childInfo.OuterXml);
					}
				}
			}
		}

		public SpreadsheetCell addChild(Rect sizePos, string style, string name)
		{
			SpreadsheetCell cell = createChildCell(sizePos, style, name);
			m_layout.addCell(cell);
			if(name != null && name.Length >0)
				m_namedCells[name] = cell;

			return cell;
		}

		public SpreadsheetCell addChild(IGuiWidget cellChild, int x, int y, int w, int h, string style, string name)
		{
			SpreadsheetCell cell = addChild(new Rect(x,y,w,h), style, name);
			cellChild.ParentLayout = this;
			cell.Child = cellChild;
			return cell;
		}

		public override void addChild(object child, string location)
		{
			Debug.Assert(location != null, "location param cannot be null");

			string myLocation = NameAddress.getFirstAddress(location);
			SpreadsheetCell cell = (SpreadsheetCell)m_namedCells[myLocation];
			if(cell == null)
				throw new Exception("Invalid location name");
			if(cell.Child != null && !NameAddress.isSingleAddress(location))
			{
				if(cell.Child is ILayoutManager)
					((ILayoutManager)cell.Child).addChild(child, NameAddress.getRemainingAddress(location));
				else
					throw new Exception("Specified a NameAddress for a location that doesn't support NameAddress type functionality");
			}
			else
			{
				Debug.Assert(child is IGuiWidget, "child must be type of IGuiWidget");
				if(cell.Child != null)
					throw new Exception("Adding a child to a cell that has a child is an error");

				IGuiWidget sizingChild = null;
				if(child is IGuiWidget)
					sizingChild = (IGuiWidget)child;
				cell.Child = sizingChild;
				sizingChild.ParentLayout = this;
			}
		}

		public override void addChild(object child, XmlElement layoutInfo)
		{
			// TODO:  Add WinFormSpreadsheetLayout.Swaf.Gui.Layout.ILayoutManager.addChild implementation
		}

		public override void removeChild(object child)
		{
			// TODO:  Add WinFormSpreadsheetLayout.Swaf.Gui.Layout.ILayoutManager.removeChild implementation
		}

		public override void applyContent(object context)
		{
			//The base implementation doesn't need to apply any content.
		}

		public override object Ctl
		{
			get
			{
				//The base implementation doesn't have its own Ctl that it uses for itself.
				return null;
			}
		}



		protected abstract SpreadsheetCell createChildCell(Rect sizePos, string style, string name);
	}

	public abstract class SpreadsheetCell : Cell
	{
		protected IGuiWidget m_child;
		protected string m_name;

		public SpreadsheetCell(IGuiWidget child, Rect sheetRect, string style, string name)
			:base(sheetRect)
		{
			m_name = name;
			m_child = child;
			handleStyleDefinition(style);
		}

		public abstract void handleStyleDefinition(string style);

		public virtual IGuiWidget Child{get{return m_child;} set{m_child = value;}}

		public string Name {get{return m_name;}}

		public override NumericPair MinSize 
		{
			get
			{
				return new NumericPair();
			}
		}

		public override NumericPair MaxSize
		{
			get
			{
				return new NumericPair();
			}
		}

		public void resize()
		{
			if(m_child != null)
			{
				m_child.sizePositionTo(CellRect);
			}
		}
	}
}
